// OpenGL video output, added by regret

/* changelog:
 update 2: create a class
 update 1: create (ref: bsnes)
*/

// TODO: osd
#ifndef NO_OPENGL

//#include "burner.h"
#include "vid_opengl.h"

#ifdef _MSC_VER
#pragma comment(lib, "opengl32")
#endif

typedef BOOL (APIENTRY *PFNWGLSWAPINTERVALFARPROC)(int);

class VideoWGL : public Video, OpenGL {
public:
	bool fullSwitch;
	HDC display;
	HGLRC wglcontext;

	unsigned int gameImageWidth, gameImageHeight;
	unsigned int imageWidth, imageHeight;
	RECT rd;

	void setVSync(int interval)
	{
		const char* extensions = (const char *)glGetString(GL_EXTENSIONS);
		if (strstr(extensions, "WGL_EXT_swap_control") == 0) {
			return;
		}

		PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC)glGetProcAddress("wglSwapIntervalEXT");
		if (wglSwapIntervalEXT) {
			wglSwapIntervalEXT(interval);
		}
	}

	void release()
	{
		OpenGL::term();

		if (wglcontext) {
			wglDeleteContext(wglcontext);
			wglcontext = 0;
		}
	}

	int exit()
	{
		release();
		return 0;
	}

	int textureInit()
	{
		gameImageWidth = vidSupport.getWidth();
		gameImageHeight = vidSupport.getHeight();

		unsigned int textureWidth = vidSupport.rounded_power_of_two(gameImageWidth * filter.zoom(filter.filterId));
		unsigned int textureHeight = vidSupport.rounded_power_of_two(gameImageHeight * filter.zoom(filter.filterId));

		OpenGL::resize(textureWidth, textureHeight);

		return 0;
	}

	void clear()
	{
		OpenGL::clear();
	}

	int init()
	{
		exit();

		hVidWnd = hScrnWnd;

		// set fullscreen
		vidScrnDepth = 32;
		vidScrnWidth = vidWidth; vidScrnHeight = vidHeight;
		if (vidFullscreen) {
			if (scrnSetFull(vidFullscreen)) {
				return 1;
			}
			fullSwitch = true;
		}
		else if (fullSwitch) {
			scrnSetFull(false);
			fullSwitch = false;
		}

		GLuint pixel_format;
		PIXELFORMATDESCRIPTOR pfd;
		memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
		pfd.nSize      = sizeof(PIXELFORMATDESCRIPTOR);
		pfd.nVersion   = 1;
		pfd.dwFlags    = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
		pfd.iPixelType = PFD_TYPE_RGBA;

		display = GetDC(hVidWnd);
		pixel_format = ChoosePixelFormat(display, &pfd);
		SetPixelFormat(display, pixel_format, &pfd);

		wglcontext = wglCreateContext(display);
		wglMakeCurrent(display, wglcontext);

		OpenGL::init();

		if (filter.enable) {
			if (filter.init(filter.filterId, vidSupport.getRotate())) {
				dprintf(("  * Error: Couldn't initialise pixel filter.\n"));
			}
		}

		// Initialize the buffer surfaces
		if (textureInit()) {
			return 1;
		}

		clear();

		OpenGL::setfilter(filter.linear);

		setVSync(vidVSync);

		imageWidth = imageHeight = 0;

		return 0;
	}

	int scale(void* rect, unsigned width, unsigned height)
	{
		return vidSupport.scaleImage(rect, width, height);
	}

	int render()
	{
		getClientScreenRect(hVidWnd, &rd);

		unsigned ww = rd.right - rd.left;
		unsigned wh = rd.bottom - rd.top;

		scale(&rd, gameImageWidth, gameImageHeight);

		unsigned newImageWidth  = vidSupport.getRotate() ? (rd.bottom - rd.top) : (rd.right - rd.left);
		unsigned newImageHeight = vidSupport.getRotate() ? (rd.right - rd.left) : (rd.bottom - rd.top);

		if (imageWidth != newImageWidth || imageHeight != newImageHeight) {
			imageWidth  = newImageWidth;
			imageHeight = newImageHeight;

			// Set the size of the image on the PC screen
			unsigned vpx, vpy, vpw, vph;
			if (vidFullscreen) {
				vpx = rd.left;
				vpy = rd.top;
				vpw = rd.right - rd.left;
				vph = rd.bottom - rd.top;
			} else {
				vpx = (ww - (rd.right - rd.left)) / 2;
				vpy = (wh - (rd.bottom - rd.top)) / 2;
				vpw = rd.right - rd.left;
				vph = rd.bottom - rd.top;
			}
			OpenGL::setview(vpx, vpy, vpw, vph, imageWidth, imageHeight);
		}

		unsigned int* pd;
		unsigned int pitch;
		OpenGL::lock(pd, pitch);

		filter.apply((unsigned char*)pd, pitch);

		unsigned int inwidth = gameImageWidth;
		unsigned int inheight = gameImageHeight;

		inwidth *= filter.zoom(filter.filterId);
		inheight *= filter.zoom(filter.filterId);

		OpenGL::refresh(inwidth, inheight, imageWidth, imageHeight);

		SwapBuffers(display);

		return 0;
	}

	int frame(bool redraw)
	{
		render();
		return 0;
	}

	int paint(int validate)
	{
		if (!vidFullscreen) {
			RECT rect = { 0, 0, 0, 0 };
			getClientScreenRect(hVidWnd, &rect);

			scale(&rect, vidSupport.getWidth(), vidSupport.getHeight());
			if (rd.left != rect.left || rd.right != rect.right
				|| rd.top != rect.top || rd.bottom != rect.bottom) {
				validate |= 2;
			}
		}

		if (validate & 2) {
			render();
		}

		if (validate & 1) {
			ValidateRect(hVidWnd, 0);
		}
		return 0;
	}

	int get(void* info)
	{
		InterfaceInfo* pInfo = (InterfaceInfo*)info;
		if (!pInfo) {
			return 1;
		}

		if (vidFullscreen) {
			if (vidTripleBuffer) {
				IntInfoAddStringModule(pInfo, L"Using a triple buffer");
			} else {
				IntInfoAddStringModule(pInfo, L"Using a double buffer");
			}
		}

		if (filter.linear) {
			IntInfoAddStringModule(pInfo, L"Applying linear filter");
		} else {
			IntInfoAddStringModule(pInfo, L"Applying pointer filter");
		}

		return 0;
	}

	int set(const wchar_t* name, const void* value)
	{
		if (!wcscmp(name, L"Filter")) {
			OpenGL::setfilter(filter.linear);
		}
		return 0;
	}

	VideoWGL() {
		fullSwitch = false;
		display = 0;
		wglcontext = 0;

		gameImageWidth = gameImageHeight = 0;
		imageWidth = imageHeight = 0;
		rd.left = rd.right = rd.top = rd.bottom = 0;
	}

	~VideoWGL() {
		exit();
	}
};

#endif
